#include <tinx/task.h>
#include <tinx/lock.h>
#include <tinx/assert.h>
#include <tinx/list.h>

void mutex_init(mutex_t *mutex, u32 value)
{
    mutex->value = value;
    list_init(&mutex->waiters);
}

void lock_init(lock_t *lock)
{
    lock->holder = NULL;
    lock->count = 0;

    mutex_init(&lock->mutex, 1);
}

void mutex_lock(mutex_t *mutex)
{
    task_t *task = running_task();
    while (mutex->value == 0)
    {
        list_append(&mutex->waiters, &task->node);
        task_block(task, TASK_BLOCKED);
    }
    assert(mutex->value == 1);
    mutex->value--;
}

void lock_acquire(lock_t *lock)
{
    task_t *task = running_task();
    if (task == lock->holder)
    {
        lock->count++;
    }
    else
    {
        mutex_lock(&lock->mutex);
        lock->holder = task;
        assert(lock->count == 0);
        lock->count++;
    }
}

void mutex_unlock(mutex_t *mutex)
{
    assert(mutex->value == 0);

    if (!list_empty(&mutex->waiters))
    {
        list_node_t *node = list_pop(&mutex->waiters);
        task_t *waiter = element_entry(task_t, node, node);
        task_unblock(waiter, 0);
    }

    mutex->value++;
}

void lock_release(lock_t *lock)
{
    assert(lock->holder == running_task())
    assert(lock->count > 0);

    lock->count--;

    if (lock->count == 0)
    {
        lock->holder = NULL;
        mutex_unlock(&lock->mutex);
    }
}